<template>
  <div v-if="visible" class="cropper-modal" @click="handleModalClick">
    <div class="cropper-container" @click.stop>
      <div class="cropper-header">
        <h3>裁剪头像</h3>
        <button class="close-btn" @click="handleCancel">✕</button>
      </div>
      
      <div class="cropper-content">
        <div class="cropper-wrapper">
          <canvas ref="canvasRef" class="cropper-canvas"></canvas>
          <div 
            ref="cropBoxRef" 
            class="crop-box"
            :style="cropBoxStyle"
            @mousedown="startDrag"
            @touchstart.stop.prevent="startDrag"
          >
            <div 
              class="crop-handle crop-handle-nw" 
              @mousedown.stop="startResize($event, 'nw')"
              @touchstart.stop.prevent="startResize($event, 'nw')"
            ></div>
            <div 
              class="crop-handle crop-handle-ne" 
              @mousedown.stop="startResize($event, 'ne')"
              @touchstart.stop.prevent="startResize($event, 'ne')"
            ></div>
            <div 
              class="crop-handle crop-handle-sw" 
              @mousedown.stop="startResize($event, 'sw')"
              @touchstart.stop.prevent="startResize($event, 'sw')"
            ></div>
            <div 
              class="crop-handle crop-handle-se" 
              @mousedown.stop="startResize($event, 'se')"
              @touchstart.stop.prevent="startResize($event, 'se')"
            ></div>
          </div>
        </div>
        
        <div class="cropper-preview">
          <h4>预览</h4>
          <div class="preview-container">
            <canvas ref="previewRef" class="preview-canvas"></canvas>
          </div>
        </div>
      </div>
      
      <div class="cropper-footer">
        <button class="btn btn-cancel" @click="handleCancel">取消</button>
        <button class="btn btn-save" @click="handleSave">保存</button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, watch, nextTick, onMounted, onUnmounted } from 'vue'
import { toastError } from '@/utils/toast'

const props = defineProps({
  visible: {
    type: Boolean,
    default: false
  },
  imageFile: {
    type: File,
    default: null
  }
})

const emit = defineEmits(['update:visible', 'save', 'cancel'])

const canvasRef = ref(null)
const previewRef = ref(null)
const cropBoxRef = ref(null)

const image = ref(null)
const cropBoxStyle = ref({
  left: '50%',
  top: '50%',
  width: '200px',
  height: '200px',
  transform: 'translate(-50%, -50%)'
})

let isDragging = false
let isResizing = false
let resizeCorner = ''
let dragStart = { x: 0, y: 0 }
let cropBoxPos = { left: 0, top: 0, width: 200, height: 200 }
let cropBoxStart = { left: 0, top: 0, width: 0, height: 0 }
let canvasWidth = 600
let canvasHeight = 400
let imageScale = 1
let imageOffset = { x: 0, y: 0 }
let drawWidth = 0
let drawHeight = 0

// 监听文件变化，加载图片
watch(() => props.imageFile, async (file) => {
  if (file && props.visible) {
    await loadImage(file)
  }
}, { immediate: true })

// 监听可见性变化
watch(() => props.visible, async (visible) => {
  if (visible && props.imageFile) {
    await nextTick()
    await loadImage(props.imageFile)
  }
})

// 加载图片
const loadImage = (file) => {
  return new Promise((resolve) => {
    const reader = new FileReader()
    reader.onload = (e) => {
      const img = new window.Image()
      img.onload = () => {
        image.value = img
        drawCanvas()
        updatePreview()
        resolve()
      }
      img.src = e.target.result
    }
    reader.readAsDataURL(file)
  })
}

// 绘制画布
const drawCanvas = () => {
  if (!canvasRef.value || !image.value) return
  
  const canvas = canvasRef.value
  const ctx = canvas.getContext('2d')
  
  // 根据容器实际可见尺寸确定画布显示大小，避免CSS缩放导致裁剪框错位
  const wrapper = canvas.parentElement
  const wrapperRect = wrapper.getBoundingClientRect()
  const displayWidth = Math.max(280, Math.round(wrapperRect.width)) // 最小宽度兜底
  const displayHeight = Math.round(displayWidth * 2 / 3) // 4:3 视觉比例，更贴合头像裁剪
  
  // 记录“CSS像素”下的画布尺寸（与裁剪框同一坐标系）
  canvasWidth = displayWidth
  canvasHeight = displayHeight
  
  // 适配高分屏，内部像素 = CSS像素 * dpr，但绘制使用CSS像素坐标系
  const dpr = window.devicePixelRatio || 1
  canvas.width = Math.round(displayWidth * dpr)
  canvas.height = Math.round(displayHeight * dpr)
  canvas.style.width = `${displayWidth}px`
  canvas.style.height = `${displayHeight}px`
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
  
  // 计算图片缩放以适应画布
  const imgAspect = image.value.width / image.value.height
  const canvasAspect = canvasWidth / canvasHeight
  
  if (imgAspect > canvasAspect) {
    drawWidth = canvasWidth
    drawHeight = canvasWidth / imgAspect
  } else {
    drawHeight = canvasHeight
    drawWidth = canvasHeight * imgAspect
  }
  
  imageScale = image.value.width / drawWidth
  imageOffset.x = (canvasWidth - drawWidth) / 2
  imageOffset.y = (canvasHeight - drawHeight) / 2
  
  // 绘制图片
  ctx.clearRect(0, 0, canvasWidth, canvasHeight)
  ctx.drawImage(image.value, imageOffset.x, imageOffset.y, drawWidth, drawHeight)
  
  // 初始化裁剪框位置（居中，正方形，可以在整个画布范围内）
  // 最大裁剪框尺寸可以是整个画布区域
  const maxCropSize = Math.min(canvasWidth, canvasHeight)
  const cropSize = Math.max(100, maxCropSize * 0.8) // 最小100px，初始大小为80%的画布尺寸
  
  cropBoxPos.width = cropSize
  cropBoxPos.height = cropSize
  
  // 裁剪框居中在画布上（可以在画布的任何位置）
  const cropLeft = (canvasWidth - cropSize) / 2
  const cropTop = (canvasHeight - cropSize) / 2
  
  cropBoxPos.left = Math.max(0, Math.min(cropLeft, canvasWidth - cropSize))
  cropBoxPos.top = Math.max(0, Math.min(cropTop, canvasHeight - cropSize))
  
  updateCropBoxStyle()
}

// 更新裁剪框样式
const updateCropBoxStyle = () => {
  cropBoxStyle.value = {
    left: `${cropBoxPos.left}px`,
    top: `${cropBoxPos.top}px`,
    width: `${cropBoxPos.width}px`,
    height: `${cropBoxPos.height}px`
  }
  updatePreview()
}

// 更新预览
const updatePreview = () => {
  if (!previewRef.value || !image.value) return
  
  const canvas = previewRef.value
  const ctx = canvas.getContext('2d')
  const previewSize = 120
  
  canvas.width = previewSize
  canvas.height = previewSize
  
  // 清空画布，填充白色背景
  ctx.fillStyle = '#ffffff'
  ctx.fillRect(0, 0, previewSize, previewSize)
  
  // 计算裁剪区域在画布上的坐标
  const cropBoxLeft = cropBoxPos.left
  const cropBoxTop = cropBoxPos.top
  const cropBoxWidth = cropBoxPos.width
  const cropBoxHeight = cropBoxPos.height
  
  // 计算裁剪区域与图片的交集
  const imageLeft = imageOffset.x
  const imageTop = imageOffset.y
  const imageRight = imageOffset.x + drawWidth
  const imageBottom = imageOffset.y + drawHeight
  
  const cropLeft = Math.max(cropBoxLeft, imageLeft)
  const cropTop = Math.max(cropBoxTop, imageTop)
  const cropRight = Math.min(cropBoxLeft + cropBoxWidth, imageRight)
  const cropBottom = Math.min(cropBoxTop + cropBoxHeight, imageBottom)
  
  // 如果有交集，绘制图片部分
  if (cropRight > cropLeft && cropBottom > cropTop) {
    // 计算在原始图片中的坐标
    const srcX = (cropLeft - imageLeft) * imageScale
    const srcY = (cropTop - imageTop) * imageScale
    const srcWidth = (cropRight - cropLeft) * imageScale
    const srcHeight = (cropBottom - cropTop) * imageScale
    
    // 计算在预览canvas中的目标位置
    const dstX = ((cropLeft - cropBoxLeft) / cropBoxWidth) * previewSize
    const dstY = ((cropTop - cropBoxTop) / cropBoxHeight) * previewSize
    const dstWidth = ((cropRight - cropLeft) / cropBoxWidth) * previewSize
    const dstHeight = ((cropBottom - cropTop) / cropBoxHeight) * previewSize
    
    // 确保坐标有效
    if (srcX >= 0 && srcY >= 0 && srcWidth > 0 && srcHeight > 0) {
      ctx.drawImage(
        image.value,
        srcX, srcY, srcWidth, srcHeight,
        dstX, dstY, dstWidth, dstHeight
      )
    }
  }
}

// 开始拖拽
const startDrag = (e) => {
  if (isResizing) return
  isDragging = true
  const rect = canvasRef.value.getBoundingClientRect()
  const clientX = e.touches ? e.touches[0].clientX : e.clientX
  const clientY = e.touches ? e.touches[0].clientY : e.clientY
  dragStart.x = clientX - rect.left - cropBoxPos.left
  dragStart.y = clientY - rect.top - cropBoxPos.top
  document.addEventListener('mousemove', handleDrag)
  document.addEventListener('mouseup', stopDrag)
  document.addEventListener('touchmove', handleDrag, { passive: false })
  document.addEventListener('touchend', stopDrag)
  e.preventDefault()
}

// 拖拽中
const handleDrag = (e) => {
  if (isResizing) return
  if (!isDragging) return
  
  const rect = canvasRef.value.getBoundingClientRect()
  const clientX = e.touches ? e.touches[0].clientX : e.clientX
  const clientY = e.touches ? e.touches[0].clientY : e.clientY
  let newLeft = clientX - rect.left - dragStart.x
  let newTop = clientY - rect.top - dragStart.y
  
  // 确保裁剪框不超出画布区域（允许在整个画布范围内）
  const minX = 0
  const maxX = canvasWidth - cropBoxPos.width
  const minY = 0
  const maxY = canvasHeight - cropBoxPos.height
  
  newLeft = Math.max(minX, Math.min(newLeft, maxX))
  newTop = Math.max(minY, Math.min(newTop, maxY))
  
  cropBoxPos.left = newLeft
  cropBoxPos.top = newTop
  updateCropBoxStyle()
  e.preventDefault && e.preventDefault()
}

// 停止拖拽
const stopDrag = () => {
  isDragging = false
  isResizing = false
  resizeCorner = ''
  document.removeEventListener('mousemove', handleDrag)
  document.removeEventListener('mousemove', handleResize)
  document.removeEventListener('mouseup', stopDrag)
  document.removeEventListener('touchmove', handleDrag)
  document.removeEventListener('touchmove', handleResize)
  document.removeEventListener('touchend', stopDrag)
}

// 开始调整大小
const startResize = (e, corner) => {
  isResizing = true
  resizeCorner = corner
  cropBoxStart = {
    left: cropBoxPos.left,
    top: cropBoxPos.top,
    width: cropBoxPos.width,
    height: cropBoxPos.height
  }
  const rect = canvasRef.value.getBoundingClientRect()
  const clientX = e.touches ? e.touches[0].clientX : e.clientX
  const clientY = e.touches ? e.touches[0].clientY : e.clientY
  dragStart.x = clientX - rect.left
  dragStart.y = clientY - rect.top
  document.addEventListener('mousemove', handleResize)
  document.addEventListener('mouseup', stopDrag)
  document.addEventListener('touchmove', handleResize, { passive: false })
  document.addEventListener('touchend', stopDrag)
  e.preventDefault()
}

// 调整大小中
const handleResize = (e) => {
  if (!isResizing) return
  
  const rect = canvasRef.value.getBoundingClientRect()
  const clientX = e.touches ? e.touches[0].clientX : e.clientX
  const clientY = e.touches ? e.touches[0].clientY : e.clientY
  const currentX = clientX - rect.left
  const currentY = clientY - rect.top
  const deltaX = currentX - dragStart.x
  const deltaY = currentY - dragStart.y
  
  // 画布边界（允许裁剪框在整个画布范围内）
  const canvasMinX = 0
  const canvasMaxX = canvasWidth
  const canvasMinY = 0
  const canvasMaxY = canvasHeight
  
  // 限制最小尺寸
  const minSize = 50
  // 最大尺寸可以是整个画布区域
  const maxSize = Math.min(canvasMaxX - canvasMinX, canvasMaxY - canvasMinY)
  
  let newLeft = cropBoxStart.left
  let newTop = cropBoxStart.top
  let newWidth = cropBoxStart.width
  let newHeight = cropBoxStart.height
  
  // 根据拖拽的角计算新的位置和尺寸
  switch (resizeCorner) {
    case 'nw': // 左上角
      newLeft = cropBoxStart.left + deltaX
      newTop = cropBoxStart.top + deltaY
      newWidth = cropBoxStart.width - deltaX
      newHeight = cropBoxStart.height - deltaY
      break
    case 'ne': // 右上角
      newTop = cropBoxStart.top + deltaY
      newWidth = cropBoxStart.width + deltaX
      newHeight = cropBoxStart.height - deltaY
      break
    case 'sw': // 左下角
      newLeft = cropBoxStart.left + deltaX
      newWidth = cropBoxStart.width - deltaX
      newHeight = cropBoxStart.height + deltaY
      break
    case 'se': // 右下角
      newWidth = cropBoxStart.width + deltaX
      newHeight = cropBoxStart.height + deltaY
      break
  }
  
  // 计算新尺寸（保持正方形）
  let size = Math.min(newWidth, newHeight)
  
  // 限制尺寸范围（最小50px，最大可以是整个图片区域）
  if (size < minSize) {
    size = minSize
  }
  if (size > maxSize) {
    size = maxSize
  }
  
  // 根据拖拽的角，确定固定点（对角点）并计算新位置
  switch (resizeCorner) {
    case 'nw': // 拖拽左上角，固定右下角
      newLeft = cropBoxStart.left + cropBoxStart.width - size
      newTop = cropBoxStart.top + cropBoxStart.height - size
      break
    case 'ne': // 拖拽右上角，固定左下角
      newLeft = cropBoxStart.left
      newTop = cropBoxStart.top + cropBoxStart.height - size
      break
    case 'sw': // 拖拽左下角，固定右上角
      newLeft = cropBoxStart.left + cropBoxStart.width - size
      newTop = cropBoxStart.top
      break
    case 'se': // 拖拽右下角，固定左上角
      newLeft = cropBoxStart.left
      newTop = cropBoxStart.top
      break
  }
  
  // 确保裁剪框的四个角都在画布范围内的辅助函数
  const constrainCropBox = () => {
    let adjustedLeft = newLeft
    let adjustedTop = newTop
    let adjustedSize = size
    
    // 循环调整直到所有角都在范围内
    for (let i = 0; i < 3; i++) { // 最多调整3次
      // 计算四个角的坐标
      const topLeftX = adjustedLeft
      const topLeftY = adjustedTop
      const topRightX = adjustedLeft + adjustedSize
      const bottomLeftY = adjustedTop + adjustedSize
      const bottomRightX = adjustedLeft + adjustedSize
      const bottomRightY = adjustedTop + adjustedSize
      
      // 检查所有角是否都在画布范围内
      const allCornersInRange = 
        topLeftX >= canvasMinX && topLeftY >= canvasMinY &&
        topRightX <= canvasMaxX && 
        bottomLeftY <= canvasMaxY &&
        bottomRightX <= canvasMaxX && bottomRightY <= canvasMaxY
      
      if (allCornersInRange) {
        newLeft = adjustedLeft
        newTop = adjustedTop
        size = adjustedSize
        return
      }
      
      // 修正左边界
      if (topLeftX < canvasMinX) {
        adjustedLeft = canvasMinX
      }
      
      // 修正上边界
      if (topLeftY < canvasMinY) {
        adjustedTop = canvasMinY
      }
      
      // 修正右边界（缩小尺寸或移动位置）
      const rightEdge = adjustedLeft + adjustedSize
      if (rightEdge > canvasMaxX) {
        const overflow = rightEdge - canvasMaxX
        // 尝试向左移动
        if (adjustedLeft > canvasMinX) {
          const canMove = Math.min(overflow, adjustedLeft - canvasMinX)
          adjustedLeft -= canMove
          adjustedSize -= (overflow - canMove)
        } else {
          // 无法移动，只能缩小
          adjustedSize -= overflow
        }
      }
      
      // 修正下边界（缩小尺寸或移动位置）
      const bottomEdge = adjustedTop + adjustedSize
      if (bottomEdge > canvasMaxY) {
        const overflow = bottomEdge - canvasMaxY
        // 尝试向上移动
        if (adjustedTop > canvasMinY) {
          const canMove = Math.min(overflow, adjustedTop - canvasMinY)
          adjustedTop -= canMove
          adjustedSize -= (overflow - canMove)
        } else {
          // 无法移动，只能缩小
          adjustedSize -= overflow
        }
      }
      
      // 确保尺寸有效（最小尺寸，最大可以到整个画布区域）
      adjustedSize = Math.max(minSize, adjustedSize)
      const maxPossibleSize = Math.min(canvasMaxX - canvasMinX, canvasMaxY - canvasMinY)
      adjustedSize = Math.min(adjustedSize, maxPossibleSize)
    }
    
    // 应用最终调整
    newLeft = Math.max(canvasMinX, Math.min(adjustedLeft, canvasMaxX - adjustedSize))
    newTop = Math.max(canvasMinY, Math.min(adjustedTop, canvasMaxY - adjustedSize))
    size = adjustedSize
  }
  
  // 应用边界约束
  constrainCropBox()
  
  // 确保最小尺寸
  if (size < minSize) {
    const availableWidth = canvasMaxX - canvasMinX
    const availableHeight = canvasMaxY - canvasMinY
    const maxSize = Math.min(availableWidth, availableHeight)
    
    if (maxSize >= minSize) {
      size = minSize
      // 根据固定点重新计算位置，确保在画布范围内
      switch (resizeCorner) {
        case 'nw':
          newLeft = Math.max(canvasMinX, Math.min(canvasMaxX - size, cropBoxStart.left + cropBoxStart.width - size))
          newTop = Math.max(canvasMinY, Math.min(canvasMaxY - size, cropBoxStart.top + cropBoxStart.height - size))
          break
        case 'ne':
          newLeft = Math.max(canvasMinX, Math.min(canvasMaxX - size, cropBoxStart.left))
          newTop = Math.max(canvasMinY, Math.min(canvasMaxY - size, cropBoxStart.top + cropBoxStart.height - size))
          break
        case 'sw':
          newLeft = Math.max(canvasMinX, Math.min(canvasMaxX - size, cropBoxStart.left + cropBoxStart.width - size))
          newTop = Math.max(canvasMinY, Math.min(canvasMaxY - size, cropBoxStart.top))
          break
        case 'se':
          newLeft = Math.max(canvasMinX, Math.min(canvasMaxX - size, cropBoxStart.left))
          newTop = Math.max(canvasMinY, Math.min(canvasMaxY - size, cropBoxStart.top))
          break
      }
      // 再次应用边界约束
      constrainCropBox()
    } else {
      size = maxSize
      newLeft = canvasMinX + ((canvasMaxX - canvasMinX) - size) / 2
      newTop = canvasMinY + ((canvasMaxY - canvasMinY) - size) / 2
    }
  }
  
  // 最终确保在画布范围内
  newLeft = Math.max(canvasMinX, Math.min(newLeft, canvasMaxX - size))
  newTop = Math.max(canvasMinY, Math.min(newTop, canvasMaxY - size))
  
  newWidth = size
  newHeight = size
  
  cropBoxPos.left = newLeft
  cropBoxPos.top = newTop
  cropBoxPos.width = newWidth
  cropBoxPos.height = newHeight
  updateCropBoxStyle()
}

// 取消
const handleCancel = () => {
  emit('update:visible', false)
  emit('cancel')
}

// 点击模态框背景关闭
const handleModalClick = (e) => {
  if (e.target.classList.contains('cropper-modal')) {
    handleCancel()
  }
}

// 保存
const handleSave = () => {
  if (!image.value) return
  
  // 计算裁剪区域在画布上的坐标
  const cropBoxLeft = cropBoxPos.left
  const cropBoxTop = cropBoxPos.top
  const cropBoxWidth = cropBoxPos.width
  const cropBoxHeight = cropBoxPos.height
  
  // 创建临时canvas，大小为裁剪框的尺寸（包括空白区域）
  const tempCanvas = document.createElement('canvas')
  tempCanvas.width = cropBoxWidth * imageScale
  tempCanvas.height = cropBoxHeight * imageScale
  const ctx = tempCanvas.getContext('2d')
  
  // 填充白色背景（如果原图是透明背景，这里可以改为透明）
  ctx.fillStyle = '#ffffff'
  ctx.fillRect(0, 0, tempCanvas.width, tempCanvas.height)
  
  // 计算裁剪区域与图片的交集
  const imageLeft = imageOffset.x
  const imageTop = imageOffset.y
  const imageRight = imageOffset.x + drawWidth
  const imageBottom = imageOffset.y + drawHeight
  
  const cropLeft = Math.max(cropBoxLeft, imageLeft)
  const cropTop = Math.max(cropBoxTop, imageTop)
  const cropRight = Math.min(cropBoxLeft + cropBoxWidth, imageRight)
  const cropBottom = Math.min(cropBoxTop + cropBoxHeight, imageBottom)
  
  // 如果有交集，绘制图片部分
  if (cropRight > cropLeft && cropBottom > cropTop) {
    // 计算在原始图片中的坐标
    const srcX = (cropLeft - imageLeft) * imageScale
    const srcY = (cropTop - imageTop) * imageScale
    const srcWidth = (cropRight - cropLeft) * imageScale
    const srcHeight = (cropBottom - cropTop) * imageScale
    
    // 计算在输出canvas中的目标位置
    const dstX = ((cropLeft - cropBoxLeft) / cropBoxWidth) * tempCanvas.width
    const dstY = ((cropTop - cropBoxTop) / cropBoxHeight) * tempCanvas.height
    const dstWidth = (srcWidth / imageScale) / cropBoxWidth * tempCanvas.width
    const dstHeight = (srcHeight / imageScale) / cropBoxHeight * tempCanvas.height
    
    // 绘制图片
    if (srcX >= 0 && srcY >= 0 && srcWidth > 0 && srcHeight > 0) {
      ctx.drawImage(
        image.value,
        srcX, srcY, srcWidth, srcHeight,
        dstX, dstY, dstWidth, dstHeight
      )
    }
  }
  
  // 转换为Blob
  tempCanvas.toBlob((blob) => {
    if (!blob) {
      toastError('裁剪失败，请重试')
      return
    }
    
    // 创建File对象
    const file = new File([blob], props.imageFile.name, {
      type: props.imageFile.type,
      lastModified: Date.now()
    })
    
    emit('save', file)
    emit('update:visible', false)
  }, props.imageFile.type, 0.95)
}

// 清理
onUnmounted(() => {
  stopDrag()
  document.removeEventListener('mousemove', handleDrag)
  document.removeEventListener('mousemove', handleResize)
  document.removeEventListener('mouseup', stopDrag)
})
</script>

<style scoped>
.cropper-modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10000;
  animation: fadeIn 0.2s;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.cropper-container {
  background: white;
  border-radius: 12px;
  width: 90%;
  max-width: 800px;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
  animation: slideUp 0.3s;
}

@keyframes slideUp {
  from {
    transform: translateY(20px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

.cropper-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20px 24px;
  border-bottom: 1px solid #e5e7eb;
}

.cropper-header h3 {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: #1f2937;
}

.close-btn {
  background: none;
  border: none;
  font-size: 24px;
  color: #6b7280;
  cursor: pointer;
  padding: 0;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  transition: all 0.2s;
}

.close-btn:hover {
  background: #f3f4f6;
  color: #1f2937;
}

.cropper-content {
  display: flex;
  gap: 24px;
  padding: 24px;
  overflow: auto;
}

.cropper-wrapper {
  flex: 1;
  position: relative;
  border: 2px solid #e5e7eb;
  border-radius: 8px;
  overflow: hidden;
  background: #f9fafb;
}

.cropper-canvas {
  display: block;
  max-width: 100%;
  height: auto;
  /* 🔑 移动端：允许我们拦截触摸移动用于拖拽 */
  touch-action: none;
}

.crop-box {
  position: absolute;
  border: 2px solid #667eea;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);
  cursor: move;
  box-sizing: border-box;
  /* 🔑 移动端：禁用浏览器默认手势，保证可拖拽 */
  touch-action: none;
}

.crop-handle {
  position: absolute;
  width: 12px;
  height: 12px;
  background: #667eea;
  border: 2px solid white;
  border-radius: 50%;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

.crop-handle-nw {
  top: -6px;
  left: -6px;
  cursor: nw-resize;
}

.crop-handle-ne {
  top: -6px;
  right: -6px;
  cursor: ne-resize;
}

.crop-handle-sw {
  bottom: -6px;
  left: -6px;
  cursor: sw-resize;
}

.crop-handle-se {
  bottom: -6px;
  right: -6px;
  cursor: se-resize;
}

.cropper-preview {
  width: 160px;
  flex-shrink: 0;
}

.cropper-preview h4 {
  margin: 0 0 12px 0;
  font-size: 14px;
  font-weight: 600;
  color: #6b7280;
}

.preview-container {
  width: 120px;
  height: 120px;
  border: 2px solid #e5e7eb;
  border-radius: 50%;
  overflow: hidden;
  background: #f9fafb;
  display: flex;
  align-items: center;
  justify-content: center;
}

.preview-canvas {
  width: 120px;
  height: 120px;
  border-radius: 50%;
}

.cropper-footer {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  padding: 20px 24px;
  border-top: 1px solid #e5e7eb;
}

.btn {
  padding: 10px 24px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  border: none;
  transition: all 0.2s;
}

.btn-cancel {
  background: #f3f4f6;
  color: #374151;
}

.btn-cancel:hover {
  background: #e5e7eb;
}

.btn-save {
  background: #667eea;
  color: white;
}

.btn-save:hover {
  background: #5568d3;
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}

/* 响应式设计 */
@media (max-width: 768px) {
  .cropper-content {
    flex-direction: column;
    padding: 16px;
  }
  
  .cropper-preview {
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  
  .preview-container {
    margin-top: 8px;
  }
}
</style>

